import os
import socket
import json
import requests
from dotenv import load_dotenv
from typing import List, Dict

# -------------------------- 1. 环境配置与依赖加载 --------------------------
# 加载.env文件（仅读取博查API密钥）
load_dotenv(override=True)  # 强制覆盖系统环境变量
BOCHA_API_KEY = os.getenv("BOCHA_API_KEY", "")  # 博查API密钥（必填）

# 调试：打印读取到的密钥（确认是否加载成功，正式环境可删除）
print(f"[调试] 读取到的BOCHA_API_KEY：{BOCHA_API_KEY}")

# 服务器基础配置
HOST = "0.0.0.0"  # 允许所有IP访问
PORT = 8888        # 与客户端保持一致
BUFFER_SIZE = 4096 # 数据接收缓冲区
#
#
# # -------------------------- 2. 核心搜索逻辑（博查真实搜索+模拟回退） --------------------------
# def real_search(keyword: str, page: int = 1) -> List[Dict]:
#     """使用博查AI搜索API执行真实搜索（国内可用，无需翻墙）"""
#     if not BOCHA_API_KEY:
#         raise ValueError("博查API密钥未配置（请检查.env文件）")
#
#     try:
#         # 博查API请求参数（参考官方文档：https://open.bochaai.com/docs）
#         url = "https://api.bochaai.com/v1/web-search"
#         headers = {
#             "Authorization": f"Bearer {BOCHA_API_KEY}",
#             "Content-Type": "application/json"
#         }
#         params = {
#             "q": keyword,       # 搜索关键词
#             "page": page,       # 页码（从1开始）
#             "page_size": 10     # 每页结果数（最大25）
#         }
#
#         # 发送请求并处理响应
#         response = requests.post(
#             url=url,
#             headers=headers,
#             params=params,
#             timeout=10  # 超时时间（避免长期阻塞）
#         )
#         response.raise_for_status()  # 触发HTTP错误（如401密钥无效）
#         api_data = response.json()
#
#         # 解析结果（与模拟数据格式对齐，确保客户端兼容）
#         real_results = []
#         # 博查API返回结果在 data.items 中（具体结构以官方文档为准）
#         for item in api_data.get("data", {}).get("items", []):
#             real_results.append({
#                 "title": item.get("title", "无标题"),
#                 "url": item.get("url", "无链接"),
#                 "summary": item.get("content", "无摘要")[:300]  # 截取300字避免过长
#             })
#         return real_results
#
#     except Exception as e:
#         # 捕获所有可能的错误（网络问题、密钥无效、API限制等）
#         raise Exception(f"博查API调用失败：{str(e)}")
#
#
def mock_search(keyword: str, page: int = 1, page_size: int = 5) -> List[Dict]:
    """模拟搜索（真实搜索失败时回退用）"""
    mock_data_pool = [
        # Python相关
        {
            "title": "Python基础教程 - 菜鸟教程",
            "url": "https://www.runoob.com/python/python-tutorial.html",
            "summary": "Python是解释型、面向对象编程语言，本教程从环境搭建到实战案例，适合零基础入门。"
        },
        {
            "title": "Python官方文档 - Python.org",
            "url": "https://docs.python.org/3/",
            "summary": "Python官方权威文档，包含语法详解、标准库用法、高级特性（装饰器、生成器等）。"
        },
        # 通用话题（扩展匹配范围）
        {
            "title": "如何培养兴趣爱好",
            "url": "https://example.com/hobby-cultivation",
            "summary": "喜欢一件事需要耐心，从了解到深入，逐步建立持续的热情。"
        },
        {
            "title": "表达喜欢的几种方式",
            "url": "https://example.com/express-like",
            "summary": "无论是语言还是行动，真诚是表达喜欢的核心要素。"
        },
        {
            "title": "今日新闻摘要",
            "url": "https://example.com/today-news",
            "summary": "今日国内重要新闻：多地迎来降温，专家提醒注意保暖；科技领域新突破，AI应用再升级。"
        }
    ]

    # 模糊匹配关键词（不区分大小写）
    matched_data = [
        item for item in mock_data_pool
        if keyword.lower() in item["title"].lower()
        or keyword.lower() in item["summary"].lower()
    ]

    # 分页处理
    start_idx = (page - 1) * page_size
    end_idx = start_idx + page_size
    return matched_data[start_idx:end_idx]


def perform_search(keyword: str, page: int = 1) -> Dict:
    """搜索入口：优先博查真实搜索，失败回退到模拟数据"""
    try:
        # 尝试真实搜索
        real_results = real_search(keyword, page)
        return {
            "status": "success",  # 真实搜索成功
            "keyword": keyword,
            "page": page,
            "data": real_results,
            # 修正：博查每页最多10条，超过则有下一页
            "has_more": len(real_results) >= 10,
            "msg": "已获取实时搜索结果"
        }
    except Exception as e:
        # 真实搜索失败，回退到模拟数据
        print(f"[警告] 真实搜索失败：{str(e)}（自动使用模拟数据）")
        mock_results = mock_search(keyword, page)
        return {
            "status": "fallback",  # 已回退到模拟
            "keyword": keyword,
            "page": page,
            "data": mock_results,
            # 修正：模拟数据每页最多5条，超过则有下一页
            "has_more": len(mock_results) >= 5,
            "msg": f"实时搜索暂不可用：{str(e)[:20]}..."
        }

# -------------------------- 3. 服务器通信逻辑 --------------------------
def handle_client(conn: socket.socket):
    """处理客户端连接：接收请求→解析→搜索→返回结果"""
    print(f"新客户端连接：{conn.getpeername()}")
    try:
        while True:
            # 接收客户端请求
            req_data = conn.recv(BUFFER_SIZE).decode("utf-8")
            if not req_data:
                print(f"客户端断开：{conn.getpeername()}")
                break

            # 解析JSON请求
            try:
                req = json.loads(req_data)
                print(f"收到请求：{req}（来自{conn.getpeername()}）")
            except json.JSONDecodeError:
                error_resp = json.dumps({
                    "status": "error",
                    "msg": "请求格式错误，请发送JSON数据"
                })
                conn.sendall(error_resp.encode("utf-8"))
                continue

            # 处理搜索请求
            if req.get("type") == "search":
                keyword = req.get("keyword", "").strip()
                page = int(req.get("page", 1))

                if not keyword:
                    error_resp = json.dumps({
                        "status": "error",
                        "msg": "关键词不能为空"
                    })
                    conn.sendall(error_resp.encode("utf-8"))
                    continue

                # 执行搜索并返回结果
                search_result = perform_search(keyword, page)
                conn.sendall(json.dumps(search_result).encode("utf-8"))

            else:
                error_resp = json.dumps({
                    "status": "error",
                    "msg": "仅支持type='search'的请求"
                })
                conn.sendall(error_resp.encode("utf-8"))

    except Exception as e:
        print(f"客户端处理异常：{str(e)}")
        error_resp = json.dumps({
            "status": "error",
            "msg": f"服务器错误：{str(e)}"
        })
        conn.sendall(error_resp.encode("utf-8"))
    finally:
        conn.close()


def start_mcp_server():
    """启动服务器主函数"""
    try:
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 允许端口复用
        server_socket.bind((HOST, PORT))
        server_socket.listen(5)

        # 显示API配置状态（只检查博查密钥）
        api_status = "已配置" if BOCHA_API_KEY else "未配置（将使用模拟数据）"
        print(f"✅ MCP搜索服务器已启动\n监听地址：{HOST}:{PORT}\n博查API配置：{api_status}")

        # 循环接受客户端连接
        while True:
            client_conn, client_addr = server_socket.accept()
            handle_client(client_conn)

    except PermissionError:
        print(f"❌ 端口{PORT}被占用，请关闭占用进程后重试（Windows：netstat -ano | findstr {PORT}）")
    except Exception as e:
        print(f"❌ 服务器启动失败：{str(e)}")
    finally:
        if 'server_socket' in locals():
            server_socket.close()

def real_search(keyword: str, page: int = 1) -> List[Dict]:
    """完美适配博查Web Search API：200成功响应，正确解析结果"""
    import os
    import requests
    BOCHA_API_KEY = os.getenv("BOCHA_API_KEY", "")
    if not BOCHA_API_KEY:
        raise ValueError("博查API密钥未配置")
    ent_domains = ["ent.sina.cn", "ent.qq.com", "ent.163.com", "yule.sohu.com", "mgtv.com"]
    try:
        url = "https://api.bochaai.com/v1/web-search"
        headers = {
            "Authorization": f"Bearer {BOCHA_API_KEY}",
            "Content-Type": "application/json; charset=utf-8"
        }
        data = {
            "query": keyword,
            "page": page,
            "page_size": 10
        }

        response = requests.post(
            url=url,
            headers=headers,
            json=data,
            timeout=15,
            verify=False
        )

        print(f"【博查API调试】状态码：{response.status_code}")
        print(f"【博查API调试】响应内容：{response.text[:500]}")  # 打印更多响应内容，确认结构

        response.raise_for_status()
        api_data = response.json()

        real_results = []
        # 核心修正1：博查成功返回的code是200，不是0！
        if api_data.get("code") == 200:
            # 核心修正2：结果在 data.webPages.value 里（从响应内容能看到webPages字段）
            web_pages = api_data.get("data", {}).get("webPages", {}).get("value", [])
            for item in web_pages:
                if any(domain in item.get("url", "") for domain in ent_domains):
                    real_results.append({
                        "title": item.get("name", "无标题"),
                        "url": item.get("url", "无链接"),
                        "summary": item.get("snippet", "无摘要").replace("\n", " ")[:300]  # 去除换行符
                    })
                
                
                # 适配博查的字段名（title→name，summary→snippet）
                real_results.append({
                    "title": item.get("name", "无标题"),  # 博查用name存标题
                    "url": item.get("url", "无链接"),
                    "summary": item.get("snippet", "无摘要")[:300]  # 博查用snippet存摘要
                })
        else:
            raise Exception(f"博查API返回错误：{api_data.get('msg', api_data.get('message', '未知错误'))}")

        print(f"【博查API成功】抓取{len(real_results)}条真实结果（剩余配额充足）")
        return real_results[:3]  # 最多返回3条

    except Exception as e:
        error_msg = f"博查调用失败：{str(e)} | 状态码：{response.status_code if 'response' in locals() else '无'}"
        print(f"【博查API错误】{error_msg}")
        raise Exception(error_msg)